package com.smartum.mpr.server.utils;

import com.smartum.mpr.shared.Category;
import com.smartum.mpr.shared.Subcategory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.http.entity.mime.MultipartEntity;

/**
 * Prepares header for the request JSON (because each request has similar structure) and also parses response till the JSON
 * body.
 */
public class JSONHelper {

    private static final String CLIENT_ID = "client_id";
    private static final String CLIENT_SECRET = "client_secret";
    private static final String RESPONSE_PARAMETER_KEY = "response";
    public static final String USER_ID = "user_id";
    public static final String AUTH_TOKEN = "auth_token";
    public static final String USER_NAME = "user_name";

    public static final String IMAEG_ID = "image_id";
    public static final String IMAEG_URL = "image_url";
    public static final String IMAEG_TIMESTAMP = "image_timestamp";
    public static final String IMAEG_USERNAME = "user_username";
    public static final String IMAEG_URL_LITTLE = "image_url_little";

    public static final String NEWS_ID = "news_id";
    public static final String NEWS_TITLE = "news_title";
    public static final String NEWS_TEXT = "news_text";
    public static final String NEWS_IMAGES = "news_image";
    public static final String NEWS_IMAGE_URL = "image_url";
    public static final String NEWS_IMAGE_URL_LITTLE = "image_url_little";

    /**
     * Prepares header for the request JSON (because each request has similar structure).
     * <p/>
     *
     * @param authToken
     * @return JSONObject with header of the following structure:
     *         {client_id:"1",client_secret:"a93d619298289dc8fbf78fcfcaa543"}
     */
    public static JSONObject getWebRequestHeader(Object authToken) throws IOException {
        final JSONObject rootObject = new JSONObject();
        final Properties connectionProperties = ConnectionPropertyReader.getConnectionProperties();

        rootObject.put(CLIENT_ID, connectionProperties.get(CLIENT_ID));
        rootObject.put(CLIENT_SECRET, connectionProperties.get(CLIENT_SECRET));
        if (authToken != null) {
            rootObject.put(AUTH_TOKEN, authToken.toString());
        }

        return rootObject;
    }

    /**
     * Parses response till the JSON body (which stores in the <code>response</code> parameter).
     *
     * @param responseFromService the whole response from service
     * @return just a JSON body
     * @throws ParseException
     */
    public static JSONObject getResponseJSON(String responseFromService) throws ParseException {
        final JSONParser jsonParser = new JSONParser();
        final Map<String, JSONObject> responseMap = (Map<String, JSONObject>) jsonParser.parse(responseFromService);

        return responseMap.get(RESPONSE_PARAMETER_KEY);
    }

    public static JSONArray getResponseJSONAsArray(String responseFromService) throws ParseException {
        final JSONParser jsonParser = new JSONParser();
        final Map<String, JSONArray> responseMap = (Map<String, JSONArray>) jsonParser.parse(responseFromService);

        return responseMap.get(RESPONSE_PARAMETER_KEY);
    }

    public static HttpURLConnection getHttpURLConnection(String serviceUrl) throws IOException {
        URL url = new URL(serviceUrl);

        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Accept", "application/json");
        conn.setRequestProperty("charset", "utf-8");
        conn.setDoOutput(true);

        return conn;
    }

    private static void sendRequest(HttpURLConnection conn, JSONObject webRequestHeader) throws IOException {

        final OutputStream outputStream = conn.getOutputStream();
        byte[] outputBytes = webRequestHeader.toString().getBytes("UTF-8");

        try {
            outputStream.write(outputBytes);
            outputStream.flush();
        } finally {
            outputStream.close();
        }
    }

    public static void sendRequestWithImage(File uploadFile, JSONObject webRequestHeader) throws IOException {

        HttpPost postRequest = new HttpPost(ConnectionPropertyReader.getAddNewsURL());
        MultipartEntity multiPartEntity = new MultipartEntity();
        multiPartEntity.addPart("json", new StringBody(webRequestHeader.toString()));

        FileBody fileBody = new FileBody(uploadFile, "application/octect-stream");
        multiPartEntity.addPart("photo", fileBody);
        postRequest.setEntity(multiPartEntity);

        executeRequest(postRequest);
    }

    public static void sendRequestOnUpdateWithImage(File uploadFile, JSONObject webRequestHeader) throws IOException {

        HttpPost postRequest = new HttpPost(ConnectionPropertyReader.getUpdateNewsPhotoURL());
        MultipartEntity multiPartEntity = new MultipartEntity();
        multiPartEntity.addPart("json", new StringBody(webRequestHeader.toString()));

        FileBody fileBody = new FileBody(uploadFile, "application/octect-stream");
        multiPartEntity.addPart("photo", fileBody);
        postRequest.setEntity(multiPartEntity);

        executeRequest(postRequest);
    }

    private static void executeRequest(HttpRequestBase requestBase) {

        InputStream responseStream = null;
        HttpClient client = new DefaultHttpClient();
        try {
            HttpResponse response = client.execute(requestBase);
            if (response != null) {
                response.getStatusLine().getStatusCode();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (responseStream != null) {
                try {
                    responseStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        client.getConnectionManager().shutdown();
    }

    private static String getResponse(HttpURLConnection conn) throws IOException {

        final BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(conn.getInputStream()));
        try {
            final StringBuilder result = new StringBuilder();
            String resultLine;
            while ((resultLine = bufferedReader.readLine()) != null) {
                result.append(resultLine);
            }

            return result.toString();
        } finally {
            bufferedReader.close();
        }
    }

    public static JSONObject buildImgRetrievalJSON(int pageSize, long timestamp, HttpServletRequest request) throws IOException {

        final HttpSession session = request.getSession(false);

        final Object authToken = session.getAttribute(JSONHelper.AUTH_TOKEN);
        final JSONObject webRequestHeader = JSONHelper.getWebRequestHeader(authToken);

        JSONObject requestObject = new JSONObject();
        requestObject.put("amount", pageSize);
        requestObject.put("timestamp", timestamp);

        webRequestHeader.put("request", requestObject);
        return webRequestHeader;
    }

    /**
     * Sends request to the server and gets response, which parses and fills {@link com.smartum.mpr.shared.User} bean.
     *
     * @param webRequestHeader json request
     * @param conn
     * @throws IOException
     */
    public static String proceedRequest(JSONObject webRequestHeader, HttpURLConnection conn) throws IOException {
        try {
            sendRequest(conn, webRequestHeader);
            return getResponse(conn);
        } finally {
            conn.disconnect();
        }
    }

    public static List<Category> proceedItemsResponse(String subcategoryName, String response, Object authToken) throws ParseException, IOException {
        final List<Category> result = new ArrayList<Category>();

        final JSONArray responseJSON = JSONHelper.getResponseJSONAsArray(response);
        for (int i = 0; i < responseJSON.size(); i++) {
            final Category category = new Category();
            final JSONObject jsonObject = (JSONObject) responseJSON.get(i);

            final Long category_id = (Long) jsonObject.get("category_id");

            category.setCategoryId(category_id);
            category.setCategoryName((String) jsonObject.get("category_name"));
            proceedSubcategories(subcategoryName, category, category_id, authToken);

            result.add(category);
        }

        return result;
    }

    private static void proceedSubcategories(String subcategoryUrl, Category category, Long category_id, Object authToken) throws IOException, ParseException {
        final JSONObject webRequestHeader = JSONHelper.getWebRequestHeader(authToken);
        JSONObject requestObject = new JSONObject();
        requestObject.put("category_id", category_id);

        webRequestHeader.put("request", requestObject);

        final HttpURLConnection conn = JSONHelper.getHttpURLConnection(subcategoryUrl);
        try {
            conn.connect();
        } catch (IOException e) {
        }

        final String response = JSONHelper.proceedRequest(webRequestHeader, conn);
        final JSONArray responseJSON = JSONHelper.getResponseJSONAsArray(response);

        List<Subcategory> subcategories = new ArrayList<Subcategory>();
        for (int i = 0; i < responseJSON.size(); i++) {
            final Subcategory subcategory = new Subcategory();
            final JSONObject jsonObject = (JSONObject) responseJSON.get(i);

            subcategory.setSubCategoryId((Long) jsonObject.get("subcategory_id"));
            subcategory.setSubCategoryName((String) jsonObject.get("subcategory_name"));

            subcategories.add(subcategory);
        }
        category.setSubcategories(subcategories);
    }
}
